/*
 * Galaxium Messenger
 *
 * Copyright (C) 2007 Paul Burton <paulburton89@gmail.com>
 * Copyright (C) 2007 Philippe Durand <draekz@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;

using Anculus.Core;

using Galaxium.Client;
using Galaxium.Core;
using Galaxium.Protocol;

using Mono.Addins;

namespace Galaxium.Gui
{
	public static class EmoticonUtility
	{
		static List<IEmoticonFactory> _factories;
		static List<IEmoticonSet> _sets;
		static IEmoticonSet _activeSet;
		static List<ICustomEmoticon> _customEmoticons;
		static string _cacheDir;
		internal static string _customDir;

		internal static IConfigurationSection _config = Configuration.Emoticon.Section;
		internal static IConfigurationSection _customConfig = _config["Custom"];
		
		//TODO: cache account/dest dicts based on a weak reference
		//static Dictionary<IEntity, OrderedDictionary<string, IEmoticon>> _cachedDicts = new Dictionary<IEntity, OrderedDictionary<string, IEmoticon>> ();
		static OrderedDictionary<string, IEmoticon> _masterDict;
		
		public static IEnumerable<ICustomEmoticon> Custom
		{
			get { return _customEmoticons; }
		}
		
		static EmoticonUtility()
		{
			_factories = new List<IEmoticonFactory>();
			_sets = new List<IEmoticonSet>();
			_activeSet = null;
			
			_cacheDir = Path.Combine(CoreUtility.GetConfigurationSubDirectory("Cache"), "Emoticons");
			_customDir = Path.Combine(CoreUtility.GetConfigurationSubDirectory("Cache"), "CustomEmoticons");
		}

		public static List<IEmoticonSet> Sets
		{
			get { return _sets; }
		}
		
		public static IEmoticonSet ActiveSet
		{
			get { return _activeSet; }
			set
			{
				_activeSet = value;
				
				if (_activeSet != null)
				{
					Message.SetEmoticons (_activeSet.Emoticons);
					_config.SetString(Configuration.Emoticon.Active.Name, _activeSet.Name);
				}
				else
				{
					Message.SetEmoticons (null);
					_config.SetString(Configuration.Emoticon.Active.Name, string.Empty);
				}
			}
		}
		
		public static void Initialize()
		{
			BaseUtility.CreateDirectoryIfNeeded(_cacheDir);
			BaseUtility.CreateDirectoryIfNeeded(_customDir);
			LoadFactories();
			LoadCustomEmoticons();
			AddinManager.ExtensionChanged += OnExtensionChanged;
		}
		
		public static void Shutdown()
		{
			//if (Directory.Exists(_cacheDir))
			//	Directory.Delete(_cacheDir, true);
		}

		static void LoadFactories()
		{
			_factories.Clear();
			
			foreach (EmoticonFactoryExtension node in AddinUtility.GetExtensionNodes("/Galaxium/Themes/Emoticon"))
			{
				IEmoticonFactory factory = node.GetFactory();
				factory.Initialize(_cacheDir);
				_factories.Add(factory);
			}
			
			LoadSets();
		}

		static void LoadSets()
		{
			_sets.Clear();
			_activeSet = null;
			
			foreach (IEmoticonFactory factory in _factories)
			{
				foreach (IEmoticonSet emoticonset in factory.Sets)
				{
					_sets.Add(emoticonset);
				
					if (emoticonset.Name == _config.GetString(Configuration.Emoticon.Active.Name, Configuration.Emoticon.Active.Default))
					{
						_activeSet = emoticonset;
						Message.SetEmoticons (_activeSet.Emoticons);
					}
				}
			}
			
			if ((_activeSet == null) && !string.IsNullOrEmpty(_config.GetString(Configuration.Emoticon.Active.Name, Configuration.Emoticon.Active.Default)))
			{
				if (_sets.Count > 0)
				{
					_activeSet = _sets[0];
					Message.SetEmoticons (_activeSet.Emoticons);
				}
				else
				{
					Log.Warn("No Emoticon Sets Found");
					Message.SetEmoticons (null);
				}
			}
		}
		
		public static void LoadCustomEmoticons ()
		{
			_customEmoticons = new List<ICustomEmoticon> ();
			
			foreach (IConfigurationSection section in _customConfig.Sections)
			{
				try
				{
					_customEmoticons.Add (new CustomEmoticon (section));
				}
				catch (Exception ex)
				{
					Log.Error (ex, "Unable to load custom emoticon");
				}
			}
			
			Message.SetCustomEmoticons (_customEmoticons);
		}
		
		public static List<IEmoticon> GetEmoticons(IAccount account, IContact dest, List<IEmoticon> known, bool outgoing)
		{
			List<IEmoticon> result = new List<IEmoticon>();
			
			// Add any emoticons we're given (received custom ones from the protocol)
			if (known != null)
				result.AddRange (known);
			
			// Add any custom emoticons
			if (outgoing)
			{
				foreach (ICustomEmoticon emot in _customEmoticons)
				{
					if (emot.Allow (account, dest))
						result.Add (emot);
				}
			}
			
			// Add emoticons from the active set
			if (_activeSet != null)
				result.AddRange (_activeSet.Emoticons);
			
			return result;
		}
		
		public static List<IEmoticon> GetCustomEmoticons (IAccount account, IContact dest, List<IEmoticon> known)
		{
			List<IEmoticon> result = new List<IEmoticon>();
			
			// Add any custom emoticons
			foreach (ICustomEmoticon emot in _customEmoticons)
			{
				if (emot.Allow (account, dest))
					result.Add (emot);
			}
			
			// Add any emoticons we're given (received custom ones from the protocol)
			if (known != null)
				result.AddRange (known);
			
			return result;
		}
		
		public static OrderedDictionary<string, IEmoticon> GetEmoticonDict(IAccount account, IContact dest, List<IEmoticon> known, bool outgoing)
		{
			if ((known == null) || (known.Count == 0))
			{
				//TODO: cache account/dest dicts based on a weak reference
				//if ((dest != null) && _cachedDicts.ContainsKey (dest))
				//	return _cachedDicts[dest];
				//if ((account != null) && _cachedDicts.ContainsKey (account))
				//	return _cachedDicts[account];
				
				if ((dest == null) && (account == null) && (_masterDict != null))
					return _masterDict;
			}
			
			List<IEmoticon> emoticons = GetEmoticons(account, dest, known, outgoing);
			OrderedDictionary<string, IEmoticon> result = new OrderedDictionary<string, IEmoticon>();
			
			foreach (IEmoticon emoticon in emoticons)
			{
				foreach (string equivalent in emoticon.Equivalents)
				{
					if (result.ContainsKey(equivalent))
						continue;
					
					int i;
					for (i = 0; i < result.Count; i++)
					{
						if (result[i].Key.Length <= equivalent.Length)
							break;
					}
					
					result.Insert(i, equivalent, emoticon);
				}
			}

			if ((known == null) || (known.Count == 0))
			{
				if ((dest == null) && (account == null))
					_masterDict = result;
				//else
				//	_cachedDicts[(dest != null) ? dest as IEntity : account as IEntity] = result;
			}
			
            return result;
		}
		
		public static ICustomEmoticon AddCustom (string filename)
		{
			return AddCustom (Path.GetFileNameWithoutExtension (filename),
			                  new string[] { string.Format (":{0}:", Path.GetFileNameWithoutExtension(filename)) },
			                  filename,
			                  null,
			                  null);
		}
		
		public static ICustomEmoticon AddCustom (string name, string[] equivalents, string filename, IEntity allowedSrc, IEntity allowedDest)
		{
			ICustomEmoticon emot = new CustomEmoticon (name, equivalents, filename, allowedSrc, allowedDest);
			
			_customEmoticons.Add (emot);
			Message.SetCustomEmoticons (_customEmoticons);
			
			return emot;
		}
		
		public static bool HaveCustom (ICustomEmoticon emot)
		{
			return File.Exists (CustomEmoticon.CacheFilename (emot.Filename));
		}
		
		public static void RemoveCustom (ICustomEmoticon emot)
		{
			if (emot is CustomEmoticon)
			{
				(emot as CustomEmoticon).Remove ();
				_customEmoticons.Remove (emot);
				
				Message.SetCustomEmoticons (_customEmoticons);
			}
			else
				Log.Error ("Custom emoticon '{0}' did not originate from EmoticonUtility", emot);
		}
		
		static void OnExtensionChanged (object o, ExtensionEventArgs args)
		{
			if (args.PathChanged ("/Galaxium/Themes/Emoticon"))
				LoadFactories ();
		}
	}
}